#include <sys/system.h>
#include <sys/string.h>
#include <sys/list.h>
#include <gal/ptgui.h>
#include <gal/surface.h>
#include <gal/dc.h>
#include <pages/pagectl.h>
#include <widgets/widget.h>
#include <widgets/container.h>
#include <pages/page.h>
#include <widgets/object.h>
#include <widgets/widget.h>
#include <widgets/container.h>

static void _ptgui_container_constructor(ptgui_container_t *container)
{
	/* set event handler and init field */
	ptgui_widget_set_event_handler(PTGUI_WIDGET(container), PTGUI_EVENT_HANDLER(ptgui_container_event_handler));
	pt_list_init(&(container->children));

	/* set focused widget to itself */
	container->focused = PTGUI_WIDGET(container);
	/* set container as focusable widget */
	PTGUI_WIDGET(container)->flag |= PTGUI_WIDGET_FLAG_FOCUSABLE;
}

static void _ptgui_container_destructor(ptgui_container_t *container)
{
	/* destroy children of container */
	ptgui_container_destroy_children(container);
}

ptgui_type_t * ptgui_container_type_get(void)
{
	static ptgui_type_t *container_type = PT_NULL;

	if (!container_type) {
		container_type = ptgui_type_create("container", PTGUI_WIDGET_TYPE, sizeof(ptgui_container_t),
										   PTGUI_CONSTRUCTOR(_ptgui_container_constructor),
										   PTGUI_DESTRUCTOR(_ptgui_container_destructor));
	}

	return container_type;
}

ptgui_page_control_t ptgui_container_dispatch_event(ptgui_container_t *container, pt_event_t *event)
{
	/* handle in child widget */
	struct pt_list_node *node;

	pt_list_foreach(node, &(container->children))
	{
		struct ptgui_widget *w;
		w = pt_list_entry(node, struct ptgui_widget, sibling);
		return w->event_handler(w, event);
	}
	return PTGUI_PAGE_TRAN_KEEP_FLAT;
}

ptgui_page_control_t ptgui_container_dispatch_mouse_event(ptgui_container_t *container, pt_event_t *event)
{
	/* handle in child widget */
	struct pt_list_node *node;
	ptgui_point_t	*point;

	pt_list_foreach(node, &(container->children))
	{
		struct ptgui_widget *w;
		w = pt_list_entry(node, struct ptgui_widget, sibling);
	
		point = (ptgui_point_t	*)event->lparam;
		if (ptgui_rect_contains_point(&(w->extent), point->x, point->y) == PT_EOK) {
			
			return w->event_handler(w, (pt_event_t *)event);
		}

	}

	return PTGUI_PAGE_TRAN_KEEP_FLAT;
}

ptgui_page_control_t ptgui_container_event_handler(ptgui_container_t *container, pt_event_t *event)
{
	switch (event->type) {
	case EV_PAGECTL_MOUSE_MOVE:
	case EV_PAGECTL_MOUSE_HOVER:
		/* handle in child widget */
		return ptgui_container_dispatch_mouse_event(container, event);
	case EV_PAGECTL_COMMAND:
	case EV_PAGECTL_KEY_ON:
	case EV_PAGECTL_KEY_OFF:
		return ptgui_container_dispatch_event(container, event);
	default:
		/* call parent widget event handler */
		return ptgui_widget_event_handler(PTGUI_WIDGET(container), event);
	}
}

void ptgui_container_children_render(ptgui_container_t *container)
{
	struct pt_list_node *node;
	struct ptgui_widget *widget;

	if (container == PT_NULL) {
		return;
	}

	pt_list_foreach(node, &(container->children))
	{
		widget = pt_list_entry(node, struct ptgui_widget, sibling);
		widget->render_handler(widget);
	}
}

void ptgui_container_children_frame(ptgui_container_t *container, pt_uint32_t fDelta)
{
	struct pt_list_node *node;
	struct ptgui_widget *widget;

	if (container == PT_NULL) {
		return;
	}

	pt_list_foreach(node, &(container->children))
	{
		widget = pt_list_entry(node, struct ptgui_widget, sibling);
		widget->frame_handler(widget, fDelta);
	}
}

void ptgui_container_init_page(ptgui_container_t *container, const struct ptgui_page_cell *page_cell)
{
	const ptgui_page_switch_view_t * const * p_sw_view;
	const ptgui_page_switch_view_t *sw_view;
	ptgui_widget_t *widget;
	ptgui_surface_t *surface;
	
	if (container == PT_NULL) {
		return;
	}

	p_sw_view = page_cell->switch_view;	
	while (*p_sw_view != PT_NULL) {
		sw_view = *p_sw_view;
		surface = ptgui_surface_create_from_file(bmp_files[sw_view->string_id]);
		widget = ptgui_widget_create(PTGUI_WIDGET_TYPE);				
		ptgui_widget_set_surface(PTGUI_WIDGET(widget), surface, 0, 0);
		ptgui_widget_set_rect(PTGUI_WIDGET(widget), &postions[sw_view->position_id]);
		ptgui_container_add_child(container, widget);

		p_sw_view++;
	}
}

/*
 * This function will add a child to a container widget
 * Note: this function will not change the widget layout
 * the layout is the responsibility of layout widget, such as box.
 */
void ptgui_container_add_child(ptgui_container_t *container, ptgui_widget_t *child)
{
	PT_ASSERT(container != PT_NULL);
	PT_ASSERT(child != PT_NULL);

	/* set parent and toplevel widget */
	child->parent = PTGUI_WIDGET(container);
	/* put widget to parent's children list */
	pt_list_append(&(container->children), &(child->sibling));
}

/* remove a child to widget */
void ptgui_container_remove_child(ptgui_container_t *container, ptgui_widget_t *child)
{
	PT_ASSERT(container != PT_NULL);
	PT_ASSERT(child != PT_NULL);

	if (child == container->focused) {
		/* set focused to itself */
		container->focused = PTGUI_WIDGET(container);

		ptgui_widget_focus(PTGUI_WIDGET(container));
	}

	/* remove widget from parent's children list */
	pt_list_remove(&(container->children), &(child->sibling));

	/* set parent and toplevel widget */
	child->parent = PT_NULL;
}

/* destroy all children of container */
void ptgui_container_destroy_children(ptgui_container_t *container)
{
	struct pt_list_node *node;

	if (container == PT_NULL) {
		return;
	}

	node = container->children.next;
	while (node != PT_NULL) {
		ptgui_widget_t *child = pt_list_entry(node, ptgui_widget_t, sibling);

		if (PTGUI_IS_CONTAINER(child)) {
			/* break parent firstly */
			child->parent = PT_NULL;

			/* destroy children of child */
			ptgui_container_destroy_children(PTGUI_CONTAINER(child));
		}

		/* remove widget from parent's children list */
		pt_list_remove(&(container->children), &(child->sibling));

		/* set parent and toplevel widget */
		child->parent = PT_NULL;

		/* destroy object and remove from parent */
		ptgui_object_destroy(PTGUI_OBJECT(child));

		node = container->children.next;
	}

	container->children.next = PT_NULL;
	container->focused = PTGUI_WIDGET(container);
	if (PTGUI_WIDGET(container)->parent != PT_NULL) {
		ptgui_widget_focus(PTGUI_WIDGET(container));
	}
}

ptgui_widget_t * ptgui_container_get_first_child(ptgui_container_t *container)
{
	ptgui_widget_t *child = PT_NULL;

	if (container->children.next != PT_NULL) {
		child = pt_list_entry(container->children.next, ptgui_widget_t, sibling);
	}

	return child;
}
